#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <opencv2/opencv.hpp>
#include <SQLiteCpp/SQLiteCpp.h>
#include <SQLiteCpp/VariadicBind.h>

#include "label_event.h"
#include "sdc_api_ext.h"
#include "sdc_def_ext.h"
#include "hw_app_log.h"

#include "global.h"
#include "yolov3_app.h"
#include "user_app.h"
#include "sdc_data_src.h"
#include "hw_app_config.h"
#include "algv.h"
#include "hw_rest_api.h"
#include "json.hpp"

#include "hw_ptz_service.hpp"
#include "test_funcs.hpp"
#include "lpr.h"

using namespace HWApp;
using json = nlohmann::json;

void generateSn(sdc_preset_pos* preset_pos, std::string& sn) {
    sn = gc_app_config.hardware_id;
    sn.append("_");
    sn.append(std::to_string(std::lrint(preset_pos->h_location)));
    sn.append("-");
    sn.append(std::to_string(std::lrint(preset_pos->v_location)));
    // sn.append("-");
    // sn.append(std::to_string(preset_pos->focus_pos));
    // sn.append("-");
    // sn.append(std::to_string(preset_pos->zoom_pos));
}

int get_sdc_preset_position() {
    std::vector<sdc_preset_info> preset_infos;
    int ret = PtzService::GetInstance().GetPresetInfos(&preset_infos);
    if (ret != HW_OK) {

        LOG_WARN("get preset position error");

        gc_app_config.has_preset = false;

        GC_CAMERA_CONFIG gcc;
        gcc.sn = gc_app_config.hardware_id;
        gcc.area = AREA;
        gc_app_config.camera_config_map[gcc.sn] = gcc;
        return HW_OK;
    }
    if (preset_infos.size() > 0) {
        gc_app_config.has_preset = true;
        for (auto &preset : preset_infos) {
            GC_CAMERA_CONFIG gcc;
            if (preset.preset_attr.preset_position.h_location == 0 && preset.preset_attr.preset_position.v_location == 0) {
                gcc.sn = gc_app_config.hardware_id;
            } else {
                generateSn(&preset.preset_attr.preset_position, gcc.sn);
            }
            gcc.area = AREA; // 默认打包的项目区域代号，如果相机进行了配置，同步相机配置后会覆盖该值
            gc_app_config.camera_config_map[gcc.sn] = gcc;
        }
    } else {

        LOG_INFO("no preset position found");

        gc_app_config.has_preset = false;

        GC_CAMERA_CONFIG gcc;
        gcc.sn = gc_app_config.hardware_id;
        gcc.area = AREA;
        gc_app_config.camera_config_map[gcc.sn] = gcc;
    }
    return HW_OK;
}

// 全局异常处理函数
void globalTerminateHandler() {
    std::cerr << "Global exception caught. Sleeping for 3 seconds..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(3));
    std::_Exit(EXIT_FAILURE);
}

int main(int argc, char* argv[])
{

    // 设置全局异常处理函数
    std::set_terminate(globalTerminateHandler);

    /* test begin */
    // if (2 > 1) {
    //     test_mnn_lpr();
    //     return HW_OK;
    // }
    /* test end */
    // 全局变量清零操作
    ResetGlobalVars();

    auto tp=std::chrono::system_clock::now();
    auto time=std::chrono::system_clock::to_time_t(tp);
    std::stringstream ss;
    ss<<std::put_time(std::localtime(&time),"%Y-%m-%d %H:%M:%S");

    app_start_time = ss.str();

    json configData;
    std::ifstream ifs("./config/app_config.json", std::fstream::in);
    if (ifs) {
        ifs >> configData;
        ifs.close();
        if (configData.contains("test")) {
            if (configData.at("test").at("enable") == 1) {
                gc_app_config.algv_host = configData.at("test").at("ip").get<std::string>() + ":" + to_string(configData.at("test").at("port").get<int>());
                LOG_INFO("set algv host to test env: %s", gc_app_config.algv_host.c_str());
            }
        }
        if (configData.contains("pro")) {
            if (configData.at("pro").at("enable") == 1) {
                gc_app_config.algv_host = configData.at("pro").at("ip").get<std::string>() + ":" + to_string(configData.at("pro").at("port").get<int>());
                LOG_INFO("set algv host to pro env: %s", gc_app_config.algv_host.c_str());
            }
        }
    }

    LOG_INFO("\n\n");
    LOG_INFO("app try start at %s, area=%s, build=%s", app_start_time.c_str(), AREA, BUILD_INFO);

    try {
        gc_db.exec("CREATE TABLE IF NOT EXISTS inout (id INTEGER PRIMARY KEY, inout_id TEXT, plate TEXT, time_str TEXT, berth TEXT, inout_type INTEGER, plate_conf INTEGER, inout_conf INTEGER, berth_conf INTEGER, plate_color TEXT)");    
    }
    catch (std::exception& e)
    {
        LOG_ERROR("SQLite exception: %s", e.what());
        return ERR;
    }
    // 运行全局变量，置为1运行状态
    g_running = 1;

    // 1. 打开文件服务连接
	if (SDC_ServiceCreate() != HW_OK) {
        LOG_ERROR("Create the SDC_OS service failed");
        return ERR;
    }
    LOG_INFO("Create the SDC_OS service successfully");

    // 2. 获取硬件ID
    sdc_hardware_id_s stHardWareParas;
    if (SDC_GetHardWareId(&stHardWareParas) != HW_OK) {
        LOG_ERROR("Get the HardWare Id failed");
        return ERR;
    }
    LOG_INFO("Get the HardWare Id successfully, hardware_id: %s", stHardWareParas.id);

    std::string hardware_id = std::string(stHardWareParas.id);
    gc_app_config.hardware_id = hardware_id;
    std::shared_ptr<HWApp::Yolov3App> app = HWApp::CreateYolov3App(HWApp::AppType::APP_TYPE_LPD);
    if (app == nullptr) {
        LOG_ERROR("Create the Lpd App failed");
        return ERR;
    }
    LOG_INFO("Create the Lpd App successfully\n");    

    // 获取预置位列表，初始化相机配置
    if (get_sdc_preset_position() != HW_OK) {
        return ERR;
    }

    GCAlgV *gc_algv = new GCAlgV();
    std::map<std::string, GC_CAMERA_CONFIG>::iterator iter = gc_app_config.camera_config_map.begin();
    while(iter != gc_app_config.camera_config_map.end()) {
        gc_algv->SyncCameraConfig(iter->first);
        if (iter->second.ip.empty()) {
            LOG_WARN("Please config ip by AlgV first, sn=%s", iter->second.sn.c_str());
        }
        iter++;
    }
    iter = gc_app_config.camera_config_map.begin();
    do {
        gc::CameraConfig *camCfg = new gc::CameraConfig(iter->second.ip);
        if (camCfg->get_camera_config_byRestApi() < 0) {
            LOG_WARN("Get_camera_config_byRestApi failed, sn=%s, ip=%s", iter->second.sn.c_str(), iter->second.ip.c_str());
        } else {
            LOG_INFO("Get_camera_config_byRestApi over, camera_mode: %d, camera_model: %d", iter->second.mode, iter->second.model);
            while(iter != gc_app_config.camera_config_map.end()) {
                gc_algv->SyncCameraConfig(iter->first);
                if (iter->second.model == GC_CAMERA_MODEL::MODEL_UNKNOWN) {
                    LOG_WARN("Please config model by AlgV first, sn=%s, ip=%s", iter->second.sn.c_str(), iter->second.ip.c_str());
                }
                iter++;
            }
        }
        delete camCfg;
    } while (0);

    iter = gc_app_config.camera_config_map.begin();
    gc_algv->GetAppConfig(iter->second.area.c_str());
    delete gc_algv;

    if (iter->second.area == "algv") {
        gc_app_config.push_to_algv = true;
    }

    int ret = std::system(("mkdir -p " + gc_app_config.base_config.alg_config.cache_dir).c_str());
    if (ret != 0) {
        LOG_ERROR("创建缓存根目录失败，请确认用户权限。%d", ret);
        return ERR;
    }
    std::system("rm -rf core"); // 删除可能存在的core文件，防止其占尽空间
    std::system("rm -rf /tmp/.lpdapp/*"); // 清空可能存在的以前使用的缓存目录
    ret = std::system(("rm -rf " + gc_app_config.base_config.alg_config.cache_dir + "*").c_str());
    if (ret != 0) {
        LOG_ERROR("清理缓存根目录失败，请确认用户权限。%d", ret);
        return ERR;
    }
    iter = gc_app_config.camera_config_map.begin();
    while(iter != gc_app_config.camera_config_map.end()) {
        std::string dir = gc_app_config.base_config.alg_config.cache_dir + iter->first;
        ret = std::system(("mkdir -p " + dir).c_str());
        if (ret != 0) {
            LOG_ERROR("创建相机缓存目录失败，请确认用户权限。%d, dir=%s", ret, dir.c_str());
            return ERR;
        }
        ret = std::system(("rm -rf " + dir + "/*.jpg").c_str());
        if (ret != 0) {
            LOG_ERROR("清理相机缓存目录失败，请确认用户权限。%d", ret);
            return ERR;
        }
        iter ++;
    }

    const char* imagePath = "./jpeg/in_1/Image20.jpg";
    // if (argc == 2) {
    //     imagePath = argv[1];
    // }
    // printf("imagePath = %s\n", imagePath);

    vector<string> imagePathList;
    // for (int i = 5; i < 30; i++)
    // {
    //     // std::string path = "./jpeg/in_1/Image" + std::to_string(i) + ".jpg";
    //     // imagePathList.push_back(path);
    // }

    // 1. 初始化Yuv依赖的资源
    std::string appName = "AIPlus";
    if (app->Init(appName) != HW_OK) {
        LOG_ERROR("Init the Lpd resource failed\n");
        app->Destroy();
        exit(-1);
        return ERR;
    }
    LOG_INFO("Init the Lpd resource successfully\n");

    // 2. 获取一个可用Yuv通道，并设置Yuv通道属性
    sdc_yuv_channel_param_s param;
    memset_s(&param, sizeof(param), 0, sizeof(param));
	param.width     = gc_app_config.base_config.alg_config.default_yuv_img_w;
	param.height    = gc_app_config.base_config.alg_config.default_yuv_img_h;
	param.fps       = gc_app_config.base_config.alg_config.subscribe_yuv_fps;
	param.on_off    = 1;
    if (app->SetYuvChanAttr(param)) {
        LOG_ERROR("Set the attribute for the Yuv channel failed\n");
        app->Destroy();
        exit(-1);
        return ERR;
    }
    LOG_INFO("Set the attribute for the Yuv channel successfully\n");

    // 3. 加载NNIE WK文件
    LOG_DEBUG("Load the Lpd NNIE model");
    std::string modelName = gc_app_config.base_config.alg_config.model_path;
    if(app->LoadModel(1, modelName) != HW_OK) {
        LOG_ERROR("Load the Lpd NNIE model failed, model_name: %s\n", modelName.c_str());
        app->Destroy();
        exit(-1);
        return ERR;
    }
    LOG_INFO("Load the Lpd NNIE model successfully, model_name: %s\n", modelName.c_str());
    
    // 4. NNIE参数初始化入口
    if (app->NNieParamInit() != HW_OK) {
        LOG_ERROR("Init the NNie parameters failed\n");
        app->Destroy();
        exit(-1);
        return ERR;
    }
    LOG_INFO("Init the NNie parameters successfully\n");

    // 5. 设置推理数据源VideoStream/ImageFile
    uint32_t yuvChannID = app->GetYuvChannelId();
    HWApp::YuvDataSrc dataType = HWApp::YuvDataSrc::DATA_SRC_VIDEO_STREAM;  // 设置数据源为VideoStream
    std::shared_ptr<HWApp::IYuvDataSrc> dataSrc;

    switch (dataType)
    {
        case HWApp::YuvDataSrc::DATA_SRC_VIDEO_STREAM:
            dataSrc = HWApp::CreateDataSrcFromVideoStream(yuvChannID);
            break;
        case HWApp::YuvDataSrc::DATA_SRC_IMAGE_FILE:
            dataSrc = HWApp::CreateDataSrcFromImageFile(imagePath);
            break;
        case HWApp::YuvDataSrc::DATA_SRC_IMAGE_FILE_STREAM:
            dataSrc = HWApp::CreateDataSrcFromImageFileStream(imagePathList, false);
            break;
        default:
            LOG_ERROR("dataType is not support");
            app->Destroy();
            exit(-1);
            return ERR;
    }

    // 初始化GCLpr
    GCLpr::GetInstance();

    // 6. 订阅Yuv数据帧并进行推理
    if (app->SubscribeYuvDataAndForward(dataSrc) != HW_OK) {
        LOG_ERROR("Subscribe the Yuv data, and forward Yuv data failed");
        exit(-1);        
        return ERR;
    }
    app->JoinThread();
    app->Destroy();
	return 0;
}
